/**
	require lib/jquery.js
	require permissionChecker.js
	require dataHandler.js
	require pull.js
	require push.js
*/
if(!QSAPI){
	var QSAPI={};
}

QSAPI.ClassAuthentication=function(instCode,qsPath,callback){
	this.instCode=instCode;
	this.qsPath=qsPath;
	this.callback=callback;
};
QSAPI.ClassAuthentication.prototype={
	login:function(userEmail,password,callbacks){
		callbacks=callbacks||{};
		var self=this;
		if(this.instCode==null){
			if(typeof callbacks.onFailure=='function'){
				callbacks.onFailure({
					errorCode:'-1',
					errorMessage:'Not initialize QSAPI.'
				});
			}
		}else{
			var authURL=this.qsPath+"apis/sdkAuth.jsp?cb=?";
			$.ajax({
				type:'GET',
				url:authURL,
				cache:false,
				timeout:10000,
				dataType:'json',
				data:{cmd:'login',encode:true,instid:this.instCode,userEmail:userEmail,password:password}, 
				success:function(data,textStatus){
					if(data.ret=='0'){
						if(typeof self.callback.afterAuthenticate=='function'){
							self.callback.afterAuthenticate(data);
						}
						if(typeof callbacks.onSuccess=='function'){
							callbacks.onSuccess({
								errorCode:'0',
								errorMessage:'Authentication is successful.'
							});
						}
					}else{
						if(typeof callbacks.onFailure=='function'){
							callbacks.onFailure({
								errorCode:'-2',
								errorMessage:'Account is incorrect.'
							});
						}
					}
				},
				error:function(XMLHttpRequest, textStatus, errorThrown){
					var returnObject={
						errorCode:'-3',
						errorMessage:'Service temporarily unavailable.'
					};
					if(textStatus=='timeout'){
						returnObject={
							errorCode:'-4',
							errorMessage:'Request timeout.'
						}
					}
					if(typeof callbacks.onFailure=='function'){
						callbacks.onFailure(returnObject);
					}
				}
			});
		}
	}
	/*,
	logout:function(){
		var authURL=this.qsPath+"apis/sdkAuth.jsp?cb=?";
		$.getJSON(authURL,{cmd:'logout'},function(o){
		})
	}*/
};

QSAPI.ClassDataManager=function(cfg){
	this.TYPE={
		PUSH:'push',
		PULL:'pull'
	};
	this.CMD={
		SUBSCRIBE:'subscribe',
		UNSUBSCRIBE:'unSubscribe'
	};
	this.cfg={
		dataPermission:null,
		exchangeMap:null,
		MICExangeMap:null,
		exchangeMarketTimeMap:null,
		pullSubSeparator:'_',
		pushSubSeparator:'|',
		qsPath:null
	};
	if(cfg){
		$.extend(true,this.cfg,cfg);
	}
	this.push=null;
	this.pull=null;
	this._type=null;
	this.__SNAPPullObj=null;
	this.dataHandler={};
	this.subscriber={};
	this.permissionChecker=null;
};

QSAPI.ClassDataManager.prototype={
	init:function(){
		this.permissionChecker = new QS.PermissionChecker({DataPermission:this.cfg.dataPermission},this.cfg.exchangeMap,this.cfg.MICExangeMap);
	},
	setCfg:function(cfg){
		if(cfg){
			$.extend(true,this.cfg,cfg);
		}
	},
	connectToPush:function(pushDomain,callbacks){//connect to push server
		var self=this;
		callbacks=callbacks||{};
		if(QSAPI.checkLogin()){
			this._type=this.TYPE.PUSH;
			this.dataHandler[this.TYPE.PUSH]=new QS.DataHandler({
				staticDataService:this.cfg.qsPath+'getStaticData.jsp',
				showAfter:false,
				pullSubSeparator:this.cfg.pullSubSeparator,
				pushSubSeparator:this.cfg.pushSubSeparator,
				gExchangeMarketTimeMap:this.cfg.exchangeMarketTimeMap
			},{
				onDataUpdate:function(gkey,needUpdateField){
					self.__updateData(self.TYPE.PUSH,gkey,needUpdateField);
				}
			});
			this.push=new QS.Push(this.dataHandler[this.TYPE.PUSH],{
				onSuccess:function(result){
					self.subscriber[self.TYPE.PUSH]=new QSAPI.Subscriber(self.push,self.permissionChecker,{type:self.TYPE.PUSH,pushSubSeparator:self.cfg.pushSubSeparator});
					if(typeof callbacks.onSuccess=='function'){
						callbacks.onSuccess(result);
					}
				},
				onFailure:callbacks.onFailure
			});
			this.push.initConnection(pushDomain);
		}else{
			if(typeof callbacks.onFailure=='function'){
				callbacks.onFailure({
					errorCode:'-2',
					errorMessage:'No authentication.'
				});
			}
		}
	},
	disconnectPush:function(){
		if(this.push!=null){
			this.push.disconnect();
		}
	},
	connectToPull:function(pullService,frequency,callbacks){//connect to pull server
		var self=this;
		callbacks=callbacks||{};
		if(QSAPI.checkLogin()){
			this._type=this.TYPE.PULL;
			this.dataHandler[this.TYPE.PULL]=new QS.DataHandler({
				staticDataService:this.cfg.qsPath+'getStaticData.jsp',
				showAfter:false,
				pullSubSeparator:this.cfg.pullSubSeparator,
				pushSubSeparator:this.cfg.pushSubSeparator,
				gExchangeMarketTimeMap:this.cfg.exchangeMarketTimeMap
			},{
				onDataUpdate:function(gkey,needUpdateField){
					self.__updateData(self.TYPE.PULL,gkey,needUpdateField);
				}
			});
			this.pull=new QS.Pull(this.dataHandler[this.TYPE.PULL],{
				onSuccess:function(result){
					self.subscriber[self.TYPE.PULL]=new QSAPI.Subscriber(self.pull,self.permissionChecker,{type:self.TYPE.PULL,pushSubSeparator:self.cfg.pushSubSeparator});
					if(typeof callbacks.onSuccess=='function'){
						callbacks.onSuccess(result);
					}
				},
				onFailure:callbacks.onFailure
			});
			this.pull.initConnection(pullService,frequency);
		}else{
			if(typeof callbacks.onFailure=='function'){
				callbacks.onFailure({
					errorCode:'-2',
					errorMessage:'Not authentication.'
				});
			}
		}
	},
	disconnectPull:function(){
		if(this.pull!=null){
			this.pull.disconnect();
		}
	},
	getQuoteData:function(tickers,callback){
		this.dataHandler[this._type].getQuoteData(tickers,callback);
	},
	getStaticData:function(tickers,callback){
		this.dataHandler[this._type].getStaticData(tickers,callback);
	},
	subscribePush:function(component,tickers,callbacks,needSubscribeLevelII){
		this.__execCommand(this.CMD.SUBSCRIBE,this.TYPE.PUSH,component,tickers,callbacks,needSubscribeLevelII);
	},
	subscribePull:function(component,tickers,callbacks){
		this.__execCommand(this.CMD.SUBSCRIBE,this.TYPE.PULL,component,tickers,callbacks);
	},
	unSubscribePush:function(component,tickers,callbacks,needSubscribeLevelII){
		this.__execCommand(this.CMD.UNSUBSCRIBE,this.TYPE.PUSH,component,tickers,callbacks,needSubscribeLevelII);
	},
	unSubscribePull:function(component,tickers,callbacks){
		this.__execCommand(this.CMD.UNSUBSCRIBE,this.TYPE.PULL,component,tickers,callbacks);
	},
	deregisterComponent:function(component){
		if(!this.__isValidComponent(component)){
			return;
		}
		for(var type in this.subscriber){
			this.subscriber[type].removeCache(component);
		}
	},
	destory:function(){
		this.disconnectPush();
		this.disconnectPull();
		this.instCode=null;
		this.push=null;
		this.pull=null;
		this.dataHandler={};
		this.subscriber={};
	},
	
	/*************************************/
	/**private method that will not open**/
	/**
	**@para tickerObject QSAPI.Ticker
	*/
	_getSnapQuoteData:function(tickerObjects,callback){
		if(this.__SNAPPullObj==null){
			this.__SNAPPullObj=new QS.Pull();
			this.__SNAPPullObj.setPullService(QSAPI.getQSPullPath()+"getPullQuote.jsp");
		}
		if(typeof this.dataHandler['SNAPQUOTE']=='undefined'){
			this.dataHandler['SNAPQUOTE']=new QS.DataHandler({
				staticDataService:this.cfg.qsPath+'getStaticData.jsp',
				showAfter:false,
				pullSubSeparator:this.cfg.pullSubSeparator,
				pushSubSeparator:this.cfg.pushSubSeparator,
				gExchangeMarketTimeMap:this.cfg.exchangeMarketTimeMap
			});
		}
		this.dataHandler['SNAPQUOTE'].getSNAPQuoteData(tickerObjects,this.__SNAPPullObj,callback);
	},
	_checkPremission:function(tickerObject){
		return this.permissionChecker.checkTickerObjectPremission(tickerObject);
	},
	_checkAutoCompletePermission:function(micCode,exchId){
		return this.permissionChecker.checkACBPermission(micCode,exchId);
	},
	_getType:function(){
		return this._type;
	},
	_getData:function(gkey,field){
		return this.dataHandler[this._type].getMarketQuoteData(gkey,field);
	},
	_getExchangeMap:function(){
		return this.cfg.exchangeMap;
	},
	_getMICExangeMap:function(){
		return this.cfg.MICExangeMap;
	},
	__execCommand:function(cmd,type,component,tickers,callbacks,needSubscribeLevelII){
		needSubscribeLevelII=needSubscribeLevelII===true?true:false;
		callbacks=callbacks||{};
		var self=this;
		var errorResult=null;
		if(!this.__isConnect(type)){
			errorResult={
				errorCode:'-1',
				errorMessage:'Not Connect.'
			};
		}else if(!this.__isValidComponent(component)){
			errorResult={
				errorCode:'-2',
				errorMessage:'Empty subscribeID of this component.'
			};
		}
		/*else if(!this.__isEmptyTickerList(tickerObjects)){
			errorResult={
				errorCode:'-3',
				errorMessage:'TickerObjects is empty.'
			};
		}*/
		if(errorResult!=null&&typeof callbacks.onFailure=='function'){
			callbacks.onFailure(errorResult);
		}else{
			if(cmd==this.CMD.SUBSCRIBE){
				this.subscriber[type].subscribe(component,tickers,callbacks,needSubscribeLevelII);
			}else if(cmd==this.CMD.UNSUBSCRIBE){
				this.subscriber[type].unSubscribe(component,tickers,callbacks,needSubscribeLevelII);
			}
		}
	},
	__updateData:function(type,gkey,needUpdateField){
		var subscriber=this.subscriber[type];
		if(subscriber){
			var subWidgets=subscriber.subWidgets[gkey];
			if(subWidgets){
				for(var w in subWidgets){
					var component=subWidgets[w];
					if(component && typeof component.updateData=='function'){
						var subscribeID=component.subscribeID;
						var tickerObjects=subscriber.subTickers[subscribeID];
						if(tickerObjects){
							for(var s in tickerObjects){
								if(tickerObjects[s].getField('gkey')==gkey){
									component.updateData(tickerObjects[s],needUpdateField);
								}
								if(tickerObjects[s].getField('eGkey')==gkey){//update ETF ticker
									component.updateData(tickerObjects[s].etfTicker,needUpdateField);
								}
							}
						}
					}
				}
			}
		}
	},
	__isValidComponent:function(component){
		if(!component||!component.subscribeID){
			return false;
		}
		return true;
	},
	__isEmptyTickerList:function(tickerList){
		if(!tickerList||tickerList.length==0){
			return false;
		}
		return true;
	},
	__isConnect:function(type){
		if(type==this.TYPE.PUSH){
			return this.push!=null;
		}else{
			return this.pull!=null;
		}
	}
	
	/*************************************/
};


$.extend(QSAPI,{
	cfg:{
		env:'stage'
	},
	instCode:null,
	DataManager:new QSAPI.ClassDataManager(),
	Authentication:null,
	loginStatus:false,
	qsPath:null,
	qsPullPath:null,
	init:function(instCode,cfg){
		var self=this;
		this.instCode=instCode;
		if(cfg){
			$.extend(true,this.cfg,cfg);
		}
		this.qsPath="//quotespeed.morningstar.com/";
		this.qsPullPath="//pullqs.morningstar.com/pullservice/pull/"
		if(this.cfg.env=='dev'){
			this.qsPath="//rtqdev.morningstar.com/opt/";
			this.qsPullPath="//rtqdev.morningstar.com/pullservice/pull/"
		}else if(this.cfg.env=='pre-stage'){
			this.qsPath="//qsstage.morningstar.com/prestage/";
			this.qsPullPath="//qsstage.morningstar.com/pullservice/pull/"
		}else if(this.cfg.env=='stage'){
			this.qsPath="//qsstage.morningstar.com/";
			this.qsPullPath="//qsstage.morningstar.com/pullservice/pull/"
		}else if(this.cfg.env=='pre-release'){
			this.qsPath="//qspre.morningstar.com/";
			this.qsPullPath="//qspre.morningstar.com/pullservice/pull/"
		}
		
		if(typeof GB_ChartDataManager!='undefined'){
			this.DataManager.Chart=GB_ChartDataManager;
		}
		
		this.Authentication=new QSAPI.ClassAuthentication(this.instCode,this.qsPath,{
			afterAuthenticate:function(o){
				if(o.ret=='0'){
					self.loginStatus=true;
					self.DataManager.setCfg({
						qsPath:self.qsPath,
						dataPermission:o.dataPermission||[],
						exchangeMap:o.gExchangeMap||{},
						MICExangeMap:o.gMicExangeMap||{},
						exchangeMarketTimeMap:o.gExchangeMarketTimeMap||{},
						pullSubSeparator:o.pullSubSeparator||'_',
						pushSubSeparator:o.pushSubSeparator||'|'
					});
					self.DataManager.init();
				}
				
			}
		});
	},
	getQSPath:function(){
		return this.qsPath;
	},
	getQSPullPath:function(){
		return this.qsPullPath;
	},
	getInstID:function(){
		return this.instCode;
	},
	checkLogin:function(){
		return this.loginStatus;
	},
	getQSVersion:function(){
		return this.cfg.env;
	},
	setLoginStatus:function(loginStatus){
		this.loginStatus=loginStatus;
	}
});